#include "copyright.h"

#include "system.h"
#include "interrupt.h"
#include "netserver.h"
#include <iostream>
#include <stdio.h>
#include <sys/time.h>


//Send message function
void SendMessage(int machineId, int mailBoxId, char *data)
{
	PacketHeader outPktHdr;
	MailHeader outMailHdr;

	outPktHdr.to = machineId;
	outMailHdr.to = mailBoxId;
	outMailHdr.from = ServerMailBoxId;
	outMailHdr.length = strlen(data) + 1;

	if(postOffice->netAddr == 0){
		bool success = postOffice->Send(outPktHdr, outMailHdr, data);
		if ( !success ) {
			printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
			interrupt->Halt();
		}
	}
}

//Send server timestamp and server forward message function
void SendServerMessage(int machineId, int mailBoxId, char *data)
{
	PacketHeader outPktHdr;
	MailHeader outMailHdr;

	outPktHdr.to = machineId;
	outMailHdr.to = mailBoxId;
	outMailHdr.from = ServerMailBoxId;
	outMailHdr.length = strlen(data) + 1;

	bool success = postOffice->Send(outPktHdr, outMailHdr, data);
	if ( !success ) {
		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
		interrupt->Halt();
	}
}


//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
//						SERVERLOCK CLASS METHODS
//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
//Constructor for serverLock class
serverLockObj::serverLockObj(char* debugName) {

	name = debugName;	//Initialize the name of lock to name assigned
	isLockFree=SERVERLOCKFREE;	// The lock are free by default. so Intialize isLockFREE to 0
	ownerMachineId=-1;	// Intially there is no owner for this lock. so it is set to -1 by default
	ownerMailBoxId=-1;
	lockWaitQueue = new List();	// Instantiating the list for queuing the reply Messages.

}
//Destructor for serverLock class
serverLockObj::~serverLockObj() {
	delete lockWaitQueue;
}

//isHeldByCurrentCaller method
bool serverLockObj::isHeldByCurrentCaller(int machineId, int mailBoxId)
{
	if((machineId == ownerMachineId) && (mailBoxId == ownerMailBoxId))	// Will check whether the lock is already owned by the caller.
	{
		return 1;			// will return 1 if both the threads are same
	}
	else
	{
		return 0;
	}
}

//Acquire method
void serverLockObj:: Acquire(int machineId, int mailBoxId)
{
	if(isHeldByCurrentCaller(machineId, mailBoxId))		   // To check whether the client trying to acquire the lock already has the lock or not.
		// if they are same there is not need to acquire lock again
		// and hence the Acquire() function ends here.
	{
		DEBUG('r', "Server: \"Acquire Method\": Lock %s already held by Client with machineId %d mailBoxId %d\n", \
																		getName(), machineId, mailBoxId);
		//Send positive reply to the calling client

		char *buf = new char[MaxMailSize];
		sprintf(buf,"%d", PositiveReply);
		SendMessage(machineId, mailBoxId, buf);
		delete[] buf;
		return;
	}

	if(isLockFree == SERVERLOCKFREE)			// Check if the lock is available for locking.
	{
		isLockFree=SERVERLOCKBUSY;			// Making the lock unavailable for the other clients.
		ownerMachineId = machineId;		//Making the current client the onwer of this lock.
		ownerMailBoxId = mailBoxId;
		DEBUG('r', "Server: \"Acquire Method\": Lock %s acquired by Client with machineId %d mailBoxId %d\n", \
																		getName(), machineId, mailBoxId);
		//Send positive reply to the calling client
		char *buf = new char[MaxMailSize];
		sprintf(buf,"%d", PositiveReply);
		SendMessage(machineId, mailBoxId, buf);
		delete[] buf;
	}
	else
	{
		/*
	DEBUG('r', "Lock %s already held by another Client. Hence Client machineId %d mailBoxId %d has to wait!! \n\n"\
				,getName(),replyMsg->outPktHdr.to, replyMsg->outMailHdr.to);
		 */
		DEBUG('r', "Server: \"Acquire Method\": Lock %s already held by another Client. Hence Client with machineId %d mailBoxId %d has to wait\n", \
																		getName(), machineId, mailBoxId);
		//Create a reply Message
		replyMessage *replyMsg = new replyMessage();
		//Populate the reply Message
		sprintf(replyMsg->data,"%d", PositiveReply);
		replyMsg->outPktHdr.to = machineId;
		replyMsg->outMailHdr.to = mailBoxId;
		replyMsg->outMailHdr.from = ServerMailBoxId;
		replyMsg->outMailHdr.length = strlen(replyMsg->data + 1);
		lockWaitQueue->Append((void*)replyMsg);// Adding the reply message to the wait queue until the lock is released.
	}
	return;
}

//Release Method
bool serverLockObj::Release(int machineId, int mailBoxId)
{
	//If lock not held by the calling client send -ve reply
	if(!(isHeldByCurrentCaller(machineId, mailBoxId)))
	{
		DEBUG('r', "Server: \"Release Method\": Invalid Release Call. Lock %s is not held by Client with machineId %d mailBoxId %d\n", \
																		getName(), machineId, mailBoxId);
		return false;
	}

	DEBUG('r', "Server: \"Release Method\": Lock %s released by Client with machineId %d mailBoxId %d\n", \
																	getName(), machineId, mailBoxId);


	replyMessage *replyMsg = (replyMessage *)lockWaitQueue->Remove();
	if(replyMsg!=NULL)
	{

		//Make the new client the woner of the lock
		ownerMachineId = replyMsg->outPktHdr.to;
		ownerMailBoxId = replyMsg->outMailHdr.to;
		DEBUG('r', "Server: \"Release Method\": Lock %s acquired by Client with machineId %d mailBoxId %d\n", \
																		getName(), ownerMachineId, ownerMailBoxId);
		//Wake the waiting client
		SendMessage(replyMsg->outPktHdr.to, replyMsg->outMailHdr.to, replyMsg->data);
		delete replyMsg;//Delete the reply message
	}
	else
	{
		isLockFree=SERVERLOCKFREE;
		ownerMachineId=-1;
		ownerMailBoxId=-1;
	}
	return true;
}


//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
//						END OF SERVERLOCK CLASS METHODS
//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------




//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
//						SERVERCONDITION CLASS METHODS
//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------

serverConditionObj::serverConditionObj(char* debugName)
{
	waitingOnLock=-1;
	name = debugName;
	cvWaitQueue= new List();
}
serverConditionObj::~serverConditionObj()
{
	delete cvWaitQueue;
}
void serverConditionObj::Wait(int conditionLock, int machineId, int mailBoxId)
{
	if(waitingOnLock==-1)
	{
		waitingOnLock=conditionLock;//there is no waiting lock in queue so we make this client wait on this lock
	}
	if(waitingOnLock!=conditionLock)//the client calling the wait on the condition does not have the same lock waiting already
	{
		DEBUG('r', "Server: \"Wait Method\": Invalid wait call on CV %s by Client with machineId %d mailBoxId %d because locks are different\n", \
																		getName(), machineId, mailBoxId);
		char *negResp=new char[MaxMailSize];
		sprintf(negResp,"%d",NegativeReply);
		SendMessage(machineId,mailBoxId,negResp);
		delete[] negResp;
		return;
	}

	DEBUG('r', "Server: \"Wait Method\": Client with machineId %d mailBoxId %d waiting on CV %s\n", \
																	machineId, mailBoxId,getName());

	//Create a reply Message
	replyMessage *replyMsg = new replyMessage();
	//Populate the reply Message
	sprintf(replyMsg->data,"%d", PositiveReply);
	replyMsg->outPktHdr.to = machineId;
	replyMsg->outMailHdr.to = mailBoxId;
	replyMsg->outMailHdr.from = ServerMailBoxId;
	replyMsg->outMailHdr.length = strlen(replyMsg->data + 1);
	cvWaitQueue->Append((void*)replyMsg);// Adding the reply message to the wait queue until the lock is released.

	//condtionLock will be released now.
	serverLockTable.serverLock[conditionLock].sLock->Release(machineId,mailBoxId);

	return;

}

void serverConditionObj::Signal(int conditionLock, int machineId, int mailBoxId)
{
	if(cvWaitQueue->IsEmpty())
	{
		char *posResp=new char[MaxMailSize];
		sprintf(posResp,"%d",PositiveReply);
		SendMessage(machineId,mailBoxId,posResp);
		delete[] posResp;

		return;
	}
	if(waitingOnLock!=conditionLock) // Locks are different. Print a debug message
	{
		DEBUG('r', "Server: \"Signal Method\": Invalid signal call on CV %s by Client with machineId %d mailBoxId %d because locks are different\n", \
																		getName(), machineId, mailBoxId);
		char *negResp=new char[MaxMailSize];
		sprintf(negResp,"%d",NegativeReply);
		SendMessage(machineId,mailBoxId,negResp);
		delete[] negResp;
		return;
	}

	replyMessage *replyMsg = (replyMessage *)cvWaitQueue->Remove();
	serverLockTable.serverLock[conditionLock].sLock->Acquire(replyMsg->outPktHdr.to,replyMsg->outMailHdr.to);
	delete replyMsg;

	char *posResp=new char[MaxMailSize];
	sprintf(posResp,"%d",PositiveReply);
	SendMessage(machineId,mailBoxId,posResp);
	delete[] posResp;

	return;
}


void serverConditionObj::Broadcast(int conditionLock, int machineId, int mailBoxId)
{
	if(cvWaitQueue->IsEmpty())
	{
		char *posResp=new char[MaxMailSize];
		sprintf(posResp,"%d",PositiveReply);
		SendMessage(machineId,mailBoxId,posResp);
		delete[] posResp;

		return;
	}
	if(waitingOnLock!=conditionLock) // Locks are different. Print a debug message
	{
		DEBUG('r', "Server: \"Broadcast Method\": Invalid broadcast call on CV %s by Client with machineId %d mailBoxId %d because locks are different\n", \
																		getName(), machineId, mailBoxId);
		char *negResp=new char[MaxMailSize];
		sprintf(negResp,"%d",NegativeReply);
		SendMessage(machineId,mailBoxId,negResp);
		delete[] negResp;
		return;
	}

	replyMessage *replyMsg;
	while(!(cvWaitQueue->IsEmpty()))
	{
		replyMsg = (replyMessage *)cvWaitQueue->Remove();
		serverLockTable.serverLock[conditionLock].sLock->Acquire(replyMsg->outPktHdr.to,replyMsg->outMailHdr.to);
		delete replyMsg;
	}

	char *posResp=new char[MaxMailSize];
	sprintf(posResp,"%d",PositiveReply);
	SendMessage(machineId,mailBoxId,posResp);
	delete[] posResp;
	return;
}
//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
//						END OF SERVERCONDITION CLASS METHODS
//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
//						SERVER_MONITORVARIABLES CLASS METHODS
//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------




serverMonitorVarObj::serverMonitorVarObj(char* debugName, int initValue)
{
	name = debugName;
	value = initValue;
}

serverMonitorVarObj::~serverMonitorVarObj()
{
	//dummy
}

void serverMonitorVarObj::GetMV(int machineId, int mailBoxId )
{
	char *buf = new char[MaxMailSize];
	sprintf(buf,"%d", value);
	DEBUG('r', "Server: \"GetMV Method\": MV %s with value %d returned to Client with machineId %d mailBoxId %d\n", \
																	getName(), value, machineId, mailBoxId);
	SendMessage(machineId, mailBoxId, buf);
	delete[] buf;
	return ;
}

void serverMonitorVarObj::SetMV(int setValue, int machineId, int mailBoxId)
{
	value=setValue;
	//Send +ve reply
	char *posResp = new char[MaxMailSize];
	sprintf(posResp,"%d",PositiveReply);
	DEBUG('r', "Server: \"SetMV Method\": MV %s set by Client with machineId %d mailBoxId %d to value %d\n", \
																	getName(), machineId, mailBoxId, value);
	SendMessage(machineId,mailBoxId,posResp);
	delete[] posResp;

	return;
}

//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
//						END OF SERVER_MONITORVARIABLES CLASS METHODS
//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------


/**
 * This function creates a serverLockObject
 */
void CreateLockFunction(int machineId,int mailBoxId,char* name)
{
	int lockIndex;

	for(int i=0;i<MAX_LOCKS;i++)
	{
		//If the lock already exists, then pass the id to caller
		if((serverLockTable.serverLock[i].valid == TRUE) && (strcmp(serverLockTable.serverLock[i].sLock->name, name) == 0))
		{
			//Increment the lockCounter
			serverLockTable.serverLock[i].lockCounter = serverLockTable.serverLock[i].lockCounter + 1;

			//If we are here that means the lock is already existing.
			char *data = new char[5];
			sprintf(data,"%d",i);
			printf("Server : CreateLockFunction : Lock with name %s is already created; hence id sent to client: %s\n",name,data);

			SendMessage(machineId, mailBoxId,data);
			delete[] data;
			return;
		}
	}


	/*If we have reached here that means the lock was not present.
	 	 we would have to create a new lock.*/
	if(( lockIndex = serverLockTable.serverLockBitMap->Find()) == -1)
	{

		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);
		printf("Server : CreateLockFunction : Maximum Lock limit is reached, Can not create the lock with name %s\n",name);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Create a server Lock
	serverLockTable.serverLock[lockIndex].sLock = new serverLockObj(name);
	serverLockTable.serverLock[lockIndex].valid = true;
	serverLockTable.serverLock[lockIndex].lockCounter = serverLockTable.serverLock[lockIndex].lockCounter + 1;
	//Send a positive reply with the lock index
	char *data=new char[5];
	sprintf(data,"%d",lockIndex);
	printf("Server : CreateLockFunction : Creating Lock with name: %s and lock-id: %s\n",name,data);

	SendMessage(machineId, mailBoxId,data);
	delete[] data;
}

/**
 * This function deals with the destroyLock checks before destroying the lock from server
 */
void DestroyLockFunction(int machineId,int mailBoxId,int lockId)
{
	//check if lockId is in a valid range
	if(lockId<0 || lockId>=MAX_LOCKS){
		printf("Server : DestroyLockFunction : Lock ID %d out of range \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a lock with the given id was already created
	if(serverLockTable.serverLock[lockId].valid == false){
		printf("Server : DestroyLockFunction : Lock ID %d does not exist \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}

	//Valid lock id. Hence decrement the lock Counter
	serverLockTable.serverLock[lockId].lockCounter = serverLockTable.serverLock[lockId].lockCounter - 1;
	//if lockCounter is 0 then destroy the lock
	if(serverLockTable.serverLock[lockId].lockCounter == 0){
		delete serverLockTable.serverLock[lockId].sLock;
		serverLockTable.serverLock[lockId].valid = false;
		serverLockTable.serverLock[lockId].lockCounter = 0;
		serverLockTable.serverLockBitMap->Clear(lockId);
	}
	//Send a +ve reply
	char *data=new char[5];
	sprintf(data,"%d",PositiveReply);
	printf("Server : DestroyLockFunction : Lock ID %d deleted \n",lockId);
	SendMessage(machineId, mailBoxId,data);
	delete[] data;

}


/**
 * This function performs the checks before proceeding with Creating Condition Variable
 */
void CreateConditionFunction(int machineId,int mailBoxId,char* name)
{

	int cvIndex;

	for(int i=0;i<MAX_CVS;i++)
	{
		//If the CV already exists, then pass the id to caller
		if((serverCVTable.serverCV[i].valid == TRUE) && (strcmp(serverCVTable.serverCV[i].sCV->name, name) == 0))
		{
			//If we are here that means the CV is already existing.
			serverCVTable.serverCV[i].cvCounter++;
			char *data = new char[5];
			sprintf(data,"%d",i);
			printf("Server : CreateConditionFunction : CV with name %s is already created; hence id sent to client: %s\n",name,data);

			SendMessage(machineId, mailBoxId,data);
			delete[] data;
			return;
		}
	}

	/*If we have reached here that means the CV was not present.
			 we would have to create a new CV.*/
	if(( cvIndex = serverCVTable.serverCVBitMap->Find()) == -1)
	{
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);
		printf("Server : CreateConditionFunction : Maximum CV limit is reached, Can not create the CV with name %s\n",name);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}

	/*Populate the cv details in the structure for new cv.*/
	serverCVTable.serverCV[cvIndex].sCV = new serverConditionObj(name);
	serverCVTable.serverCV[cvIndex].valid = true;
	serverCVTable.serverCV[cvIndex].cvCounter = serverCVTable.serverCV[cvIndex].cvCounter + 1;

	char *data=new char[5];
	sprintf(data,"%d",cvIndex);
	printf("Server : CreateConditionFunction : Creating CV with name: %s and cvId: %s\n",name,data);

	SendMessage(machineId, mailBoxId,data);
	delete[] data;
}

/**
 * This function removes the condition from the server
 */
void DestroyConditionFunction(int machineId,int mailBoxId, int cvId)
{
	//check if cvId is in a valid range
	if(cvId<0 || cvId>=MAX_CVS){
		printf("Server : DestroyConditionFunction : CV ID %d out of range \n",cvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a CV with the given id was already created
	if(serverCVTable.serverCV[cvId].valid == false){
		printf("Server : DestroyConditionFunction : CV ID %d does not exist \n",cvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}

	//Valid CV id. Hence decrement the CV Counter
	serverCVTable.serverCV[cvId].cvCounter = serverCVTable.serverCV[cvId].cvCounter - 1;
	//if CVCounter is 0 then destroy the CV
	if(serverCVTable.serverCV[cvId].cvCounter == 0){
		delete serverCVTable.serverCV[cvId].sCV;
		serverCVTable.serverCV[cvId].valid = false;
		serverCVTable.serverCV[cvId].cvCounter = 0;
		serverCVTable.serverCVBitMap->Clear(cvId);
	}
	//Send a +ve reply
	char *data=new char[5];
	sprintf(data,"%d",PositiveReply);
	printf("Server : DestroyConditionFunction : CV ID %d deleted \n",cvId);
	SendMessage(machineId, mailBoxId,data);
	delete[] data;
}

/**
 * This function performs the preliminary check before calling the Acquire call on the serverLockObject
 */
void AcquireFunction(int machineId,int mailBoxId,int lockId)
{
	//check if lockId is in a valid range
	if(lockId<0 || lockId>=MAX_LOCKS){
		printf("Server : AcquireFunction : Lock ID %d out of range \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a lock with the given id was already created
	if(serverLockTable.serverLock[lockId].valid == false){
		printf("Server : AcquireFunction : Lock ID %d does not exist \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Lock is valid Hence call the acquire method of the lock
	serverLockTable.serverLock[lockId].sLock->Acquire(machineId,mailBoxId);
	//Reply message will be sent by the Acquire method
}

/**
 * function to handle the checks for the ReleaseLock
 */
void ReleaseFunction(int machineId,int mailBoxId,int lockId)
{
	bool reply;
	//check if lockId is in a valid range
	if(lockId<0 || lockId>=MAX_LOCKS){
		printf("Server : ReleaseFunction : Lock ID %d out of range \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a lock with the given id was already created
	if(serverLockTable.serverLock[lockId].valid == false){
		printf("Server : ReleaseFunction : Lock ID %d does not exist \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Lock is valid Hence call the Release method of the lock
	reply = serverLockTable.serverLock[lockId].sLock->Release(machineId,mailBoxId);
	//Reply message will be sent by the Release method
	if(reply == false) { //-ve reply
		//Send negative reply to the calling client
		char *buf = new char[MaxMailSize];
		sprintf(buf,"%d", NegativeReply);
		printf("Server : ReleaseFunction : Lock ID %d cannot be released \n",lockId);
		SendMessage(machineId, mailBoxId, buf);
		delete[] buf;
		return;
	}
	else{//Send +ve reply
		char *buf = new char[MaxMailSize];
		sprintf(buf,"%d", PositiveReply);
		printf("Server : ReleaseFunction : Lock ID %d has been released \n",lockId);
		SendMessage(machineId, mailBoxId, buf);
		delete[] buf;

	}
}

/**
 * Function to handle the pre-checks for calling wait function on server
 */
void WaitFunction(int machineId,int mailBoxId,int lockId,int cvId)
{
	//check if lockId is in a valid range
	if(lockId<0 || lockId>=MAX_LOCKS){
		printf("Server : WaitFunction : Lock ID %d out of range \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a lock with the given id was already created
	if(serverLockTable.serverLock[lockId].valid == false){
		printf("Server : WaitFunction : Lock ID %d does not exist \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//check if cvId is in a valid range
	if(cvId<0 || cvId>=MAX_CVS){
		printf("Server : WaitFunction : CV ID %d out of range \n",cvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a CV with the given id was already created
	if(serverCVTable.serverCV[cvId].valid == false){
		printf("Server : WaitFunction : CV ID %d does not exist \n",cvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Lock and CV valid. Hence call the Wait method of the CV
	serverCVTable.serverCV[cvId].sCV->Wait(lockId,machineId,mailBoxId);
	//Reply message will be sent by the Wait method
}

/**
 * Function to perform the pre-checks before calling signal on server
 */
void SignalFunction(int machineId,int mailBoxId,int lockId,int cvId)
{
	//check if lockId is in a valid range
	if(lockId<0 || lockId>=MAX_LOCKS){
		printf("Server : SignalFunction : Lock ID %d out of range \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a lock with the given id was already created
	if(serverLockTable.serverLock[lockId].valid == false){
		printf("Server : SignalFunction : Lock ID %d does not exist \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//check if cvId is in a valid range
	if(cvId<0 || cvId>=MAX_CVS){
		printf("Server : SignalFunction : CV ID %d out of range \n",cvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a CV with the given id was already created
	if(serverCVTable.serverCV[cvId].valid == false){
		printf("Server : SignalFunction : CV ID %d does not exist \n",cvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Lock and CV valid. Hence call the Signal method of the CV
	serverCVTable.serverCV[cvId].sCV->Signal(lockId,machineId,mailBoxId);
	//Reply message will be sent by the Signal method
}

/**
 * Function to perform necessary checks for Broadcast call on server
 */
void BroadcastFunction(int machineId,int mailBoxId,int lockId,int cvId)
{
	//check if lockId is in a valid range
	if(lockId<0 || lockId>=MAX_LOCKS){
		printf("Server : BroadcastFunction : Lock ID %d out of range \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a lock with the given id was already created
	if(serverLockTable.serverLock[lockId].valid == false){
		printf("Server : BroadcastFunction : Lock ID %d does not exist \n",lockId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//check if cvId is in a valid range
	if(cvId<0 || cvId>=MAX_CVS){
		printf("Server : BroadcastFunction : CV ID %d out of range \n",cvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a CV with the given id was already created
	if(serverCVTable.serverCV[cvId].valid == false){
		printf("Server : BroadcastFunction : CV ID %d does not exist \n",cvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Lock and CV valid. Hence call the Broadcast method of the CV
	serverCVTable.serverCV[cvId].sCV->Broadcast(lockId,machineId,mailBoxId);
	//Reply message will be sent by the Broadcast method
}

/**
 * Function call to create Monitor Variables on the server side
 */
void CreateMVFunction(int machineId,int mailBoxId, char *name, int initValue)
{
	int mvIndex;

	for(int i=0;i<MAX_MVS;i++)
	{
		//If the lock already exists, then pass the id to caller
		if((serverMVTable.serverMV[i].valid == TRUE) && (strcmp(serverMVTable.serverMV[i].sMV->getName(), name) == 0))
		{
			//Increment the MVCounter
			serverMVTable.serverMV[i].mvCounter = serverMVTable.serverMV[i].mvCounter + 1;

			//If we are here that means the lock is already existing.
			char *data = new char[5];
			sprintf(data,"%d",i);
			printf("Server : CreateMV : MV with name %s is already created. Hence id sent to client: %s\n",name,data);

			SendMessage(machineId, mailBoxId,data);
			delete[] data;
			return;
		}
	}

	/*If we have reached here that means the MV was not present.
	 	 we would have to create a new MV.*/
	if(( mvIndex = serverMVTable.serverMVBitMap->Find()) == -1)
	{

		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);
		printf("Server : CreateMV : Maximum MV limit is reached, Can not create the MV with name %s\n",name);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Create a server MV
	serverMVTable.serverMV[mvIndex].sMV = new serverMonitorVarObj(name, initValue);
	serverMVTable.serverMV[mvIndex].valid = true;
	serverMVTable.serverMV[mvIndex].mvCounter = serverMVTable.serverMV[mvIndex].mvCounter + 1;


	//Send a positive reply with the lock index
	char *data=new char[5];
	sprintf(data,"%d",mvIndex);
	printf("Server : CreateMV : Creating MV with name: %s and mv-id: %s\n",name,data);

	SendMessage(machineId, mailBoxId,data);
	delete[] data;
}
/**
 * This function removes the MV from the server
 */
void DestroyMVFunction(int machineId,int mailBoxId, int mvId)
{
	//check if mvId is in a valid range
	if(mvId<0 || mvId>=MAX_MVS){
		printf("Server : DestroyMV : MV ID %d out of range \n",mvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a MV with the given id was already created
	if(serverMVTable.serverMV[mvId].valid == false){
		printf("Server : DestroyMV : MV ID %d does not exist \n",mvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}

	//Valid MV id. Hence decrement the MV Counter
	serverMVTable.serverMV[mvId].mvCounter = serverMVTable.serverMV[mvId].mvCounter - 1;
	//if MVCounter is 0 then destroy the MV
	if(serverMVTable.serverMV[mvId].mvCounter == 0){
		delete serverMVTable.serverMV[mvId].sMV;
		serverMVTable.serverMV[mvId].valid = false;
		serverMVTable.serverMV[mvId].mvCounter = 0;
		serverMVTable.serverMVBitMap->Clear(mvId);
	}
	//Send a +ve reply
	char *data=new char[5];
	sprintf(data,"%d",PositiveReply);
	printf("Server : DestroyMV : MV ID %d deleted \n",mvId);
	SendMessage(machineId, mailBoxId,data);
	delete[] data;
}

/**
 * This function sets the MV
 */

void SetMVFunction(int machineId,int mailBoxId, int mvId, int setValue)
{

	//check if mvId is in a valid range
	if(mvId<0 || mvId>=MAX_MVS){
		printf("Server : SetMV : MV ID %d out of range \n",mvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a MV with the given id was already created
	if(serverMVTable.serverMV[mvId].valid == false){
		printf("Server : SetMV : MV ID %d does not exist \n",mvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}

	//MV is valid Hence call the SetMV method of the MV
	serverMVTable.serverMV[mvId].sMV->SetMV(setValue, machineId, mailBoxId);
	//Reply message will be sent by the SetMV method
	return;
}

/**
 * This function sets the MV
 */

void GetMVFunction(int machineId,int mailBoxId, int mvId)
{
	//check if mvId is in a valid range
	if(mvId<0 || mvId>=MAX_MVS){
		printf("Server : GetMV : MV ID %d out of range \n",mvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}
	//Check if a MV with the given id was already created
	if(serverMVTable.serverMV[mvId].valid == false){
		printf("Server : GetMV : MV ID %d does not exist \n",mvId);
		//Send a -ve reply
		char *data = new char[5];
		sprintf(data,"%d",NegativeReply);

		SendMessage(machineId, mailBoxId,data);
		delete[] data;
		return;
	}

	//MV is valid Hence call the GetMV method of the MV
	serverMVTable.serverMV[mvId].sMV->GetMV(machineId, mailBoxId);
	//Reply message will be sent by the GetMV method
	return;
}


// Add an item to the list
void ListAppendFunction(int machineId,int mailBoxId, int item)
{
	int *restockItem = new int(item);
	reStockList_kernel.Append((void *)restockItem);
	//Send a +ve reply
	char *data=new char[5];
	sprintf(data,"%d",PositiveReply);
	DEBUG('r', "Server : ListAppendFunction : Item %d added \n",item);
	SendMessage(machineId, mailBoxId,data);
	delete[] data;
}

// Remove an item from the list
void ListRemoveFunction(int machineId,int mailBoxId)
{
	int myaisle;
	int *restockItem = (int *) reStockList_kernel.Remove();
	myaisle =*restockItem;
	delete restockItem;
	//Send a reply
	char *data=new char[5];
	sprintf(data,"%d",myaisle);
	DEBUG('r',"Server : ListRemoveFunction : Item %d removed from list: %s\n",data);

	SendMessage(machineId, mailBoxId,data);
	delete[] data;
}

// Find if the list is empty
void IsListEmptyFunction(int machineId,int mailBoxId)
{
	//Send a reply
	char *data=new char[5];
	sprintf(data,"%d",reStockList_kernel.IsEmpty());
	DEBUG('r',"Server : IsListEmptyFunction : return value %s\n",data);

	SendMessage(machineId, mailBoxId,data);
	delete[] data;
}



/*Client Request processing function*/
void ClientRequestProcessor(int clientMachineId, int clientMailBoxId, char *buffer)
{

	PacketHeader outPktHdr, inPktHdr;
	MailHeader outMailHdr, inMailHdr;

	//variable to hold the type of the incoming message
	int messageType;
	//variable to hold the name of locks, condition variables and monitor variables
	char *name;
	//variable to hold the id of locks, condition variables
	int lockId, cvId;
	//variable to hold the id of MV's and it's initial value
	int mvId, initialValue;
	//MV value to be set
	int setValue;
	//variable to store the list item to be appended
	int list_item;

	inPktHdr.from = clientMachineId;
	inMailHdr.from = clientMailBoxId;

	outPktHdr.to = inPktHdr.from;
	outMailHdr.to = inMailHdr.from;

	sscanf(buffer,"%d",&messageType);

	switch(messageType)
	{
	case 1:
	{
		name = new char[strlen(buffer) + 1];
		sscanf(buffer,"%d %s",&messageType,name);
		printf("Server : Received \"CreateLock\" request from Client with Machine id: %d, MailBox id: %d for lock \"%s\"\n",\
				inPktHdr.from,inMailHdr.from,name);
		CreateLockFunction(outPktHdr.to,outMailHdr.to,name);
		break;
	}
	case 2:
	{
		sscanf(buffer,"%d %d",&messageType, &lockId);
		printf("Server : Received \"DestroyLock\" request from Client with Machine id: %d, MailBox id: %d for lock id: %d\n",\
				inPktHdr.from,inMailHdr.from,lockId);
		DestroyLockFunction(outPktHdr.to,outMailHdr.to,lockId);
		break;
	}
	case 3:
	{
		name = new char[strlen(buffer) + 1];
		sscanf(buffer,"%d %s",&messageType,name);
		printf("Server : Received \"CreateCondition\" request from Client with Machine id: %d, MailBox id: %d for CV \"%s\"\n",\
				inPktHdr.from,inMailHdr.from,name);
		CreateConditionFunction(outPktHdr.to,outMailHdr.to,name);
		break;
	}
	case 4:
	{
		sscanf(buffer,"%d %d",&messageType, &cvId);
		printf("Server : Received \"DestroyCondition\" request from Client with Machine id: %d, MailBox id: %d for CV id: %d\n",\
				inPktHdr.from,inMailHdr.from,cvId);
		DestroyConditionFunction(outPktHdr.to,outMailHdr.to,cvId);
		break;
	}
	case 5:
	{
		sscanf(buffer,"%d %d",&messageType, &lockId);
		printf("Server : Received \"Acquire\" request from Client with Machine id: %d, MailBox id: %d for lock id: %d\n",\
				inPktHdr.from,inMailHdr.from,lockId);
		AcquireFunction(outPktHdr.to,outMailHdr.to,lockId);
		break;
	}
	case 6:
	{
		sscanf(buffer,"%d %d",&messageType, &lockId);
		printf("Server : Received \"Release\" request from Client with Machine id: %d, MailBox id: %d for lock id: %d\n",\
				inPktHdr.from,inMailHdr.from,lockId);
		ReleaseFunction(outPktHdr.to,outMailHdr.to,lockId);
		break;
	}
	case 7:
	{
		sscanf(buffer,"%d %d %d",&messageType, &cvId, &lockId);
		printf("Server : Received \"Wait\" request from Client with Machine id: %d, MailBox id: %d for CV id: %d with lock id: %d\n",\
				inPktHdr.from,inMailHdr.from,cvId,lockId);
		WaitFunction(outPktHdr.to,outMailHdr.to,lockId,cvId);
		break;
	}
	case 8:
	{
		sscanf(buffer,"%d %d %d",&messageType, &cvId, &lockId);
		printf("Server : Received \"Signal\" request from Client with Machine id: %d, MailBox id: %d for CV id: %d with lock id: %d\n",\
				inPktHdr.from,inMailHdr.from,cvId,lockId);
		SignalFunction(outPktHdr.to,outMailHdr.to,lockId,cvId);
		break;
	}
	case 9:
	{
		sscanf(buffer,"%d %d %d",&messageType, &cvId, &lockId);
		printf("Server : Received \"Broadcast\" request from Client with Machine id: %d, MailBox id: %d for CV id: %d with lock id: %d\n",\
				inPktHdr.from,inMailHdr.from,cvId,lockId);
		BroadcastFunction(outPktHdr.to,outMailHdr.to,lockId,cvId);
		break;
	}
	case 10:
	{
		name = new char[strlen(buffer) + 1];
		sscanf(buffer,"%d %d %s",&messageType, &initialValue, name);
		printf("Server : Received \"CreateMV\" request from Client with Machine id: %d, MailBox id: %d for MV \"%s\" with initValue %d\n",\
				inPktHdr.from,inMailHdr.from,name, initialValue);
		CreateMVFunction(outPktHdr.to, outMailHdr.to, name, initialValue);
		break;
	}
	case 11:
	{
		sscanf(buffer,"%d %d %d",&messageType, &mvId, &setValue);
		printf("Server : Received \"SetMV\" request from Client with Machine id: %d, MailBox id: %d for MV id: %d \n",\
				inPktHdr.from,inMailHdr.from,mvId);
		SetMVFunction(outPktHdr.to, outMailHdr.to, mvId, setValue);
		break;
	}
	case 12:
	{
		sscanf(buffer,"%d %d",&messageType, &mvId);
		printf("Server : Received \"GetMV\" request from Client with Machine id: %d, MailBox id: %d for MV id: %d\n",\
				inPktHdr.from,inMailHdr.from,mvId);
		GetMVFunction(outPktHdr.to, outMailHdr.to, mvId);
		break;
	}
	case 13:
	{
		sscanf(buffer,"%d %d",&messageType, &mvId);
		printf("Server : Received \"DestroyMV\" request from Client with Machine id: %d, MailBox id: %d for MV id: %d \n",\
				inPktHdr.from,inMailHdr.from,mvId);
		DestroyMVFunction(outPktHdr.to, outMailHdr.to, mvId);
		break;
	}
	case 14:
	{
		sscanf(buffer,"%d %d",&messageType, &list_item);
		printf("Server : Received \"ListAppend\" request from Client with Machine id: %d, MailBox id: %d for MV id: %d \n",\
				inPktHdr.from,inMailHdr.from,mvId);
		ListAppendFunction(outPktHdr.to, outMailHdr.to, list_item);
		break;
	}
	case 15:
	{
		printf("Server : Received \"ListRemove\" request from Client with Machine id: %d, MailBox id: %d for MV id: %d \n",\
				inPktHdr.from,inMailHdr.from,mvId);
		ListRemoveFunction(outPktHdr.to, outMailHdr.to);
		break;
	}
	case 16:
	{
		printf("Server : Received \"IsListEmpty\" request from Client with Machine id: %d, MailBox id: %d for MV id: %d \n",\
				inPktHdr.from,inMailHdr.from,mvId);
		IsListEmptyFunction(outPktHdr.to, outMailHdr.to);
		break;
	}

	default:
		printf("Server : Invalid call made to server\n");

	}//end of switch case

}//end of function



//function to process the pending msg queue
void PendingMsgProcessor()
{
	Timestamp smallestTs = 0x7FFFFFFFFFFFFFFFLL;
	Timestamp ts = 0x7FFFFFFFFFFFFFFFLL;

	//Step5a: Extract smallest timestamp
	for(int i=0; i<noOfServers; i++){
		if(ltrTable[i] < smallestTs){
			smallestTs = ltrTable[i];
		}
	}
	//Step5b: Retrieve first message from pending msg queue
	PendingMsg *pendingMsg = (PendingMsg *)pendingMsgQueue.SortedRemove((int64_t *)&ts);
	//Step6:
	while(ts <= smallestTs){
		//Process the message
		ClientRequestProcessor(pendingMsg->clientMachineId, pendingMsg->clientMailBoxId, pendingMsg->buffer);
		delete pendingMsg;
		//Retrieve the next msg from pending msg queue
		pendingMsg = (PendingMsg *)pendingMsgQueue.SortedRemove((int64_t *)&ts);
		if(pendingMsg == NULL){
			break;
		}
	}
	//Put the last msg back on to the queue
	if(pendingMsg != NULL){
		pendingMsgQueue.SortedInsert((void *)pendingMsg, (int64_t)ts);
	}
}



//Server function
void Server()
{
	PacketHeader inPktHdr;
	MailHeader inMailHdr;
	char buffer[MaxMailSize];
	char forwBuffer[MaxMailSize];
	char timeStampBuffer[MaxMailSize];
	//variable to hold the type of the incoming message
	int messageType;

	struct timeval tv;
	Timestamp ts;

	while(1)
	{
		printf("*********************************************************\n\n");
		postOffice->Receive(0, &inPktHdr, &inMailHdr, buffer);
	    printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
	    fflush(stdout);

		sscanf(buffer,"%d",&messageType);
		//Check the type of message
		if(messageType == TimestampMsg_id){
			int sendingServerId = inPktHdr.from;
			sscanf(buffer,"%d %ld %ld", &messageType, &tv.tv_sec, &tv.tv_usec);
			ts = (((long long)tv.tv_sec * (long long)1000000));
			ts = ts + (long long)tv.tv_usec;
			ts = ts*(long long)10 + (long long)sendingServerId;//to form unique timestamp
			ltrTable[sendingServerId] = ts;
			// Call the client request message processor
			PendingMsgProcessor();
		}
		else if(messageType == ServerForwardMsg_id){
			//Step1:
			int sendingServerId = inPktHdr.from;
			int msgId;
			PendingMsg *pendingMsg = new PendingMsg();
			//Add the msg to the pending message queue
			sscanf(buffer,"%d %ld %ld %d %d %[^'#']", &msgId, &tv.tv_sec, &tv.tv_usec, &(pendingMsg->clientMachineId),\
										&(pendingMsg->clientMailBoxId), pendingMsg->buffer);
			pendingMsg->forwServerId = sendingServerId;
			//Step2: Extract and Calculate timestamp
			ts = (((long long)tv.tv_sec * (long long)1000000));
			ts = ts + (long long)tv.tv_usec;
			ts = ts*(long long)10 + (long long)sendingServerId;//to form unique timestamp
			//Step3: Add the message to the pending message queue
			pendingMsgQueue.SortedInsert((void *)pendingMsg, (int64_t)ts);
			//Step4: Update the LTR table
			ltrTable[sendingServerId] = ts;
			//Since the server received Server-forwarded message, send the timestamp msg to all servers
			gettimeofday(&tv, NULL);
			sprintf(timeStampBuffer,"%d %ld %ld", TimestampMsg_id, tv.tv_sec, tv.tv_usec);
			for(int i=0; i<noOfServers; i++){
				SendServerMessage(i, 0, timeStampBuffer);
			}
			// Call the client request message processor
			PendingMsgProcessor();
		}
		else{//Client request message
			gettimeofday(&tv, NULL);
			//Get the client machine and mailbox id. These have to be padded to the client message for forwarding
			int clientMachineId = inPktHdr.from;
			int clientMailBoxId = inMailHdr.from;
			sprintf(forwBuffer,"%d %ld %ld %d %d %s#", ServerForwardMsg_id, tv.tv_sec, tv.tv_usec, clientMachineId, clientMailBoxId, buffer);
			//Forward the message to all the servers including myself
			for(int i=0; i<noOfServers; i++){
				SendServerMessage(i, 0, forwBuffer);
			}
		}
		printf("\n");
	}//end of while
}//end of function

